////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "LightHandler.hpp"

#include <SmegLight/Light/Light.hpp>
#include <SmegLight/Light/LightSystem.hpp>

#include "../../tinyxml2/tinyxml2.h"
#include "../../Canvas/SmegEditorCanvas.hpp"

#include <time.h>


////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
LightHandler::LightHandler() :
    QuadTreeOccupantHandler()
{
    m_fWander = 0.0f;
}

////////////////////////////////////////////////////////////
/// Copy Constructor
////////////////////////////////////////////////////////////
LightHandler::LightHandler( const LightHandler& _Copy ) :
    QuadTreeOccupantHandler( _Copy )
{
    m_fWander = _Copy.m_fWander;
    if ( _Copy.m_pObject ) {
        m_pObject = _Copy.m_pObject->Clone();
    }
}

////////////////////////////////////////////////////////////
/// Does an update of things that doesn't usually happen
////////////////////////////////////////////////////////////
void LightHandler::Update() {
    smeg::light::Light* lightObj = static_cast< smeg::light::Light* >( m_pObject );
    
    // Calculate the circle center
    sf::Vector2f circleCenter( lightObj->GetDirection() );
    circleCenter *= 10.0f;
    //
    // Calculate the displacement force
    sf::Vector2f displacement( 5.0f * cos( m_fWander ), 5.0f * sin( m_fWander ) );
    //
    // Change wanderAngle just a bit, so it
    // won't have the same value in the
    // next game frame.
    m_fWander += ( rand() % 100 ) * 0.01f - 0.5f;

    m_pObject->SetDirection( smeg::math::Normalize( circleCenter + displacement ) );
    m_pObject->Move( lightObj->GetDirection() * 10.0f );
}

////////////////////////////////////////////////////////////
/// Init the handler and setup the properties
/// QuadTreeOccupantHandler::InitFromGrid need to be called AFTER the specialized class
////////////////////////////////////////////////////////////
bool LightHandler::InitFromGrid( wxPropertyGrid* _pPropertyGrid ) {
    // Color
    wxPGProperty* pCurProp = _pPropertyGrid->GetProperty("Color");
    wxAny colourValue = pCurProp->GetValue();
    wxColour colourWX = colourValue.As<wxColour>();
    sf::Color colorSF(colourWX.Red(), colourWX.Green(), colourWX.Blue());
    // Spread
    pCurProp = _pPropertyGrid->GetProperty("Spread");
    wxAny spreadValue = pCurProp->GetValue();
    // Radius
    pCurProp = _pPropertyGrid->GetProperty("Radius");
    wxAny radiusValue = pCurProp->GetValue();
    float fRadius = radiusValue.As<float>() > 0.0f ? radiusValue.As<float>() : 1.0f;
                
    m_pObject = new smeg::light::Light( colorSF,
                                        sf::Vector2f(),
                                        // Angle will be set in BaseObjectHandler::Init, that is why we need to call it at the END
                                        0.0f,
                                        spreadValue.As<float>(),
                                        fRadius );

    return QuadTreeOccupantHandler::InitFromGrid( _pPropertyGrid );
}
    
////////////////////////////////////////////////////////////
/// Init the handler from an XMLElement
/// QuadTreeOccupantHandler::InitFromXML need to be called AFTER the specialized class
////////////////////////////////////////////////////////////
bool LightHandler::InitFromXML( tinyxml2::XMLElement* _pElement, const wxString& _ProjectPath ) {
    // Get the infos
    tinyxml2::XMLElement* paramElem = _pElement->FirstChildElement( "Param" );

    m_pObject = new smeg::light::Light();
    smeg::light::Light* lightObj = static_cast< smeg::light::Light* >( m_pObject );

    if ( QuadTreeOccupantHandler::InitFromXML( _pElement, _ProjectPath ) ) {
        while ( paramElem ) {
            wxString paramName = paramElem->Attribute( "name" );

            if ( "Color" == paramName ) {
                // Color
                lightObj->SetColor( sf::Color( paramElem->UnsignedAttribute( "r" ), 
                                                paramElem->UnsignedAttribute( "g" ), 
                                                paramElem->UnsignedAttribute( "b" ) ) );
            } else if ( "Spread" == paramName ) {
                // Spread
                lightObj->SetAngleSpreadDeg( paramElem->FloatAttribute( "value" ) );
            } else if ( "Radius" == paramName ) {
                // Radius
                lightObj->SetRadius( paramElem->FloatAttribute( "value" ) );
#ifdef _DEBUG
            } else if ( "DEBUG Show Inside" == paramName ) {
                // DEBUG_ShowInside
                lightObj->DEBUG_ShowInside = paramElem->BoolAttribute( "value" );
#endif
            }

            paramElem = paramElem->NextSiblingElement( "Param" );
        }
    } else {
        smeg::light::LightSystem::GetInstance()->RemoveLight( lightObj );
        m_pObject = NULL;
        return false;
    }

    return true;
}
    
////////////////////////////////////////////////////////////
/// Add the created object to the scene
////////////////////////////////////////////////////////////
void LightHandler::AddObjectToScene() {
    smeg::light::LightSystem::GetInstance()->AddLight( static_cast< smeg::light::Light* >( m_pObject ) );
}
    
////////////////////////////////////////////////////////////
/// Remove the created object from the scene
////////////////////////////////////////////////////////////
void LightHandler::RemoveObjectFromScene( bool _bDeleteObject ) {
    smeg::light::LightSystem::GetInstance()->RemoveLight( static_cast< smeg::light::Light* >( m_pObject ), _bDeleteObject );
    if ( _bDeleteObject ) {
        m_pObject = NULL;
    }
}
    
////////////////////////////////////////////////////////////
/// Return the value of a property
////////////////////////////////////////////////////////////
wxString LightHandler::GetValueAsString( const wxString& _PropertyName ) const {
    smeg::light::Light * lightObj = static_cast< smeg::light::Light* >( m_pObject );

    if ( "Color" == _PropertyName ) {
        const sf::Color& color( lightObj->GetColor() );
        return wxString::Format( "(%d,%d,%d)", color.r, color.g, color.b );
    } else if ( "Spread" == _PropertyName ) {
        return wxString::Format( "%f", lightObj->GetAngleSpreadDeg() );
    } else if ( "Radius" == _PropertyName ) {
        return wxString::Format( "%f", lightObj->GetRadius() );
#ifdef _DEBUG
    } else if ( "DEBUG Show Inside" == _PropertyName ) {
        return ( lightObj->DEBUG_ShowInside ? "true" : "false" );
#endif
    } else {
        return QuadTreeOccupantHandler::GetValueAsString( _PropertyName );
    }
}
    
////////////////////////////////////////////////////////////
/// Handle the change of a property and apply it to the object
////////////////////////////////////////////////////////////
bool LightHandler::OnPropertyChanged( wxPropertyGrid* _pPropertyGrid, wxPGProperty* _pChangedProperty ) {
    if ( QuadTreeOccupantHandler::OnPropertyChanged( _pPropertyGrid, _pChangedProperty ) ) {
        return true;
    }

    smeg::light::Light * lightObj = static_cast< smeg::light::Light* >( m_pObject );
    
    // Handle changes in values, as needed
    const wxString& propName(_pChangedProperty->GetName());
    if ( "Color" == propName ) {
        wxAny colourValue = _pChangedProperty->GetValue();
        wxColour colourWX = colourValue.As<wxColour>();
        lightObj->SetColor( sf::Color( colourWX.Red(), colourWX.Green(), colourWX.Blue(), colourWX.Alpha() ) );
    } else if ( "Spread" == propName ) {
        wxAny spreadValue = _pChangedProperty->GetValue();
        lightObj->SetAngleSpreadDeg( spreadValue.As<float>() );
        _pChangedProperty->SetValueFromString( wxString::Format("%f", lightObj->GetAngleSpreadDeg() ) );
    } else if ( "Radius" == propName ) {
        wxAny radiusValue = _pChangedProperty->GetValue();
        lightObj->SetRadius( radiusValue.As<float>() );
        _pChangedProperty->SetValueFromString( wxString::Format("%f", lightObj->GetRadius() ) );
#ifdef _DEBUG
    } else if ( "DEBUG Show Inside" == propName ) {
        wxAny debugRenderValue = _pChangedProperty->GetValue();
        lightObj->DEBUG_ShowInside = debugRenderValue.As<bool>();
#endif
    } else {
        return false;
    }

    return true;
}
   
////////////////////////////////////////////////////////////
/// Create an XML string with the value of the object
////////////////////////////////////////////////////////////
wxString LightHandler::InnerXMLString( const wxString& _ProjectPath, const wxString& _Tab ) const {
    smeg::light::Light * lightObj = static_cast< smeg::light::Light* >( m_pObject );
    const sf::Color& color( lightObj->GetColor() );

    return QuadTreeOccupantHandler::InnerXMLString( _ProjectPath, _Tab ) +
        _Tab + wxT( "<Param name=\"Color\" r=\"" ) + wxString::Format( "%d", color.r ) + wxT( "\" " ) +
                    wxT( "g=\"" ) + wxString::Format( "%d", color.g ) + wxT( "\" " ) +
                    wxT( "b=\"" ) + wxString::Format( "%d", color.b ) + wxT( "\" />\n" ) +
        _Tab + wxT( "<Param name=\"Spread\" value=\"" ) + wxString::Format( "%f", lightObj->GetAngleSpreadDeg() ) + wxT( "\" />\n" ) +
        _Tab + wxT( "<Param name=\"Radius\" value=\"" ) + wxString::Format( "%f", lightObj->GetRadius() ) + wxT( "\" />\n" )
#ifdef _DEBUG
        + _Tab + wxT( "<Param name=\"DEBUG Show Inside\" value=\"" ) + (lightObj->DEBUG_ShowInside ? wxT( "true" ) : wxT( "false" ) ) + wxT( "\" />\n" );
#else
        ;
#endif
}